feat: HTML and WASM export of Reveal slides - #10703
Conversation
|
Deployment failed for project marimo-docs with the following error: View Documentation: https://vercel.com/docs/accounts/team-members-and-roles |
|
All contributors have signed the CLA ✍️ ✅ |
Resolve configured layouts once and serialize them through static and WebAssembly mount configs so file, HTTP, and Pyodide exports share the same fallback behavior.
Keep serialized layouts pending until cells exist, then validate and materialize them through the existing renderer plugins. Initialize mounted URL state before the first render.
Hide notebook chrome for exported decks, enable static speaker view, and synchronize deep links and code controls across static and WebAssembly startup.
Flush document transactions before layout serialization, recover ambiguous sync failures through an authoritative full save, and prevent older auto-export jobs from committing after newer state.
329f813 to
5fd6a65
Compare
|
@cubic-dev-ai review please |
@peter-gy I have started the AI code review. It will take a few minutes to complete. |
There was a problem hiding this comment.
All reported issues were addressed across 70 files
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
|
@cubic-dev-ai review please |
@peter-gy I have started the AI code review. It will take a few minutes to complete. |
There was a problem hiding this comment.
All reported issues were addressed across 70 files
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
|
@cubic-dev-ai review please |
@peter-gy I have started the AI code review. It will take a few minutes to complete. |
There was a problem hiding this comment.
Pull request overview
This PR ensures that notebooks configured with the slides layout export as Reveal decks (static HTML and HTML+WASM), by carrying the serialized layout through export/mount configuration and restoring it via existing layout renderer plugins before cells render. It also strengthens export consistency by flushing pending document changes before snapshot/export and by preventing older auto-export generations/revisions from overwriting newer outputs.
Changes:
- Add
layoutto the mount config and export request/schema plumbing so static HTML and WASM exports can rehydrate the slides layout. - Validate/deserialize serialized layout data via renderer plugin validators and preserve “pending” layout until cells are available.
- Improve auto-export correctness with generation tracking, per-format revision reservation, and frontend document-change flushing before export.
Reviewed changes
Copilot reviewed 70 out of 70 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| tests/_session/state/test_session_view.py | Adds coverage for HTML auto-export staleness tracking with layout + generation handling. |
| tests/_server/test_templates_filename.py | Updates mount-config JSON parsing expectations after template changes (no trailing comma handling). |
| tests/_server/templates/utils.py | Adds helper to parse embedded mount config from exported HTML in tests. |
| tests/_server/templates/test_templates_api.py | Verifies static template includes/validates layout in mount config. |
| tests/_server/templates/snapshots/export1.txt | Snapshot update to include layout in mount config. |
| tests/_server/templates/snapshots/export2.txt | Snapshot update to include layout in mount config. |
| tests/_server/templates/snapshots/export3.txt | Snapshot update to include layout in mount config. |
| tests/_server/templates/snapshots/export4.txt | Snapshot update to include layout in mount config. |
| tests/_server/templates/snapshots/export5.txt | Snapshot update to include layout in mount config. |
| tests/_server/templates/snapshots/export6.txt | Snapshot update to include layout in mount config. |
| tests/_server/api/endpoints/test_export.py | Extends export endpoint tests to assert layout behavior and auto-export revision behavior. |
| tests/_runtime/layout/test_layout.py | Adjusts layout-file reading tests to treat invalid contents as None instead of raising. |
| tests/_pyodide/test_pyodide_session.py | Ensures Pyodide HTML export passes through and embeds serialized layout. |
| tests/_export/test_exporter.py | Expands file export tests to cover layout resolution/inlining across export kinds. |
| tests/_export/test_auto_exporter.py | Adds regression test ensuring newer auto-export writes supersede older ones. |
| tests/_cli/test_cli_export.py | Updates CLI wasm HTML export test to validate embedded layout + mode. |
| packages/openapi/src/api.ts | Updates generated OpenAPI TS types to include layout on HTML export request (and reorders schemas). |
| packages/openapi/api.yaml | Updates OpenAPI spec to document/add layout on HTML export request (and reorders schemas). |
| marimo/_templates.py | Adds layout to the mount config and passes it through static/wasm templates. |
| marimo/_session/state/session_view.py | Adds auto-export generation + tracks HTML auto-export layout to avoid missing layout-driven exports. |
| marimo/_server/templates/api.py | Validates untrusted layout input for static rendering using parse_layout_config. |
| marimo/_server/api/endpoints/export.py | Threads layout selection into HTML export + improves auto-export correctness (generation + revision). |
| marimo/_schemas/export.py | Adds layout to ExportAsHTMLRequest and implements fallback semantics (unset => read saved layout). |
| marimo/_runtime/layout/layout.py | Adds parsing/validation helpers and robust layout reading + data-URI encoding helper. |
| marimo/_pyodide/pyodide_session.py | Threads HTML export layout selection into Pyodide export path. |
| marimo/_export/requests.py | Adds layout to HTML/WASM export request dataclasses. |
| marimo/_export/file.py | Resolves/inlines layout files into data URIs for portable HTML/WASM exports. |
| marimo/_export/exporter.py | Passes layout through to templates + adds per-file revisioning/locking for auto-export writes. |
| marimo/_cli/development/commands.py | Preview command now passes app config + layout into static template. |
| marimo/_ast/app.py | Removes inline_layout_file from InternalApp (layout inlining moved to export layer). |
| frontend/src/mount.tsx | Hydrates serialized layout from mount config into layout state; adds kiosk query param handling. |
| frontend/src/core/websocket/useMarimoKernelConnection.tsx | Tracks “initial run completed” to safely enable deep-link hash resolution in wasm contexts. |
| frontend/src/core/websocket/tests/useMarimoKernelConnection.hook.test.tsx | Adds test coverage for initial-run completion tracking. |
| frontend/src/core/static/download-html.ts | Includes flushed serialized layout in static HTML download request. |
| frontend/src/core/saving/save-component.tsx | Serializes full saves via a queue and adds document resync boundary for ambiguous transaction failures. |
| frontend/src/core/saving/tests/save-component.test.ts | Tests save queue serialization and failure recovery behavior. |
| frontend/src/core/run-app.tsx | Adds option to hide header (used for exported slides). |
| frontend/src/core/meta/code-visibility.ts | Adjusts kiosk code-visibility semantics based on runtime kind (remote vs static/wasm). |
| frontend/src/core/meta/tests/code-visibility.test.tsx | Adds coverage for static kiosk code-visibility behavior. |
| frontend/src/core/layout/state.ts | Introduces layout state with “pending serialized layout” to defer materialization until cells exist. |
| frontend/src/core/layout/layout.ts | Adds helpers to resolve layout data from pending/materialized state and integrate plugin deserialization. |
| frontend/src/core/layout/tests/layout.test.ts | Adds tests for pending layout preservation and serialization fallback behavior. |
| frontend/src/core/kernel/state.ts | Adds atom to represent completion of the initial kernel run. |
| frontend/src/core/kernel/handlers.ts | Uses validated layout deserialization to build layout state safely from kernel payloads. |
| frontend/src/core/export/layout.ts | Adds helper to flush document changes before exporting and return serialized layout. |
| frontend/src/core/export/hooks.ts | Includes flushed serialized layout in auto-export requests. |
| frontend/src/components/static-html/share-modal.tsx | Includes flushed serialized layout in share/static export requests. |
| frontend/src/components/slides/speaker-view.ts | Centralizes speaker-view URL/policy logic and disables speaker view for wasm. |
| frontend/src/components/slides/reveal-component.tsx | Improves slides behavior for speaker view receivers, deep links, wasm initial hash resolution, and code-toggle binding. |
| frontend/src/components/slides/tests/speaker-view.test.ts | Adds tests for speaker-view URL building and runtime policy. |
| frontend/src/components/slides/tests/reveal-component.test.ts | Adds unit coverage for code-toggle keybinding synchronization and updated code-availability logic. |
| frontend/src/components/pages/run-page.tsx | Hides static banner/header/watermark when running exported slides. |
| frontend/src/components/editor/renderers/types.ts | Adds isLayoutType helper and strongly-typed layout type list. |
| frontend/src/components/editor/renderers/slides-layout/types.ts | Makes slides layout parsing forward-compatible by dropping invalid fields while preserving unknown keys. |
| frontend/src/components/editor/renderers/slides-layout/slides-layout.tsx | Avoids controlling deck index during receiver/deep-link initialization to prevent navigation conflicts. |
| frontend/src/components/editor/renderers/slides-layout/tests/plugin.test.ts | Updates slides plugin tests for permissive parsing and invalid-data fallback behavior. |
| frontend/src/components/editor/renderers/plugins.ts | Validates serialized layouts before deserialization; falls back to initial layout on invalid data. |
| frontend/src/components/editor/renderers/grid-layout/plugin.tsx | Makes grid layout parsing more resilient and normalizes optional fields. |
| frontend/src/components/editor/renderers/grid-layout/tests/plugin.test.ts | Adds coverage for grid layout validation/normalization behavior. |
| frontend/src/components/editor/renderers/cells-renderer.tsx | Uses shared helpers to resolve layout type/data and apply pending layout once cells exist. |
| frontend/src/components/editor/actions/export-dialog/use-export-dialog.ts | Threads layout capture into export-dialog request pipeline. |
| frontend/src/components/editor/actions/export-dialog/export-notebook.ts | Includes serialized layout in HTML export requests. |
| frontend/src/components/editor/actions/export-dialog/tests/export-notebook.test.ts | Verifies HTML export requests include layout and that non-HTML exports do not. |
| frontend/src/tests/mount.test.ts | Adds tests for layout hydration from mount config and kiosk-mode initialization. |
| frontend/e2e-tests/slides.spec.ts | Adds Playwright e2e coverage for static HTML export opening as a Reveal deck without banners/watermark. |
| docs/guides/exporting/webassembly_html.md | Documents slides behavior and speaker-notes visibility in wasm HTML exports. |
| docs/guides/exporting/static_html.md | Documents slides behavior and speaker-notes visibility in static HTML exports. |
| docs/guides/apps.md | Adds “Export slides” docs covering both static HTML and wasm exports and speaker-view limitations. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 70 out of 70 changed files in this pull request and generated 2 comments.
Suppressed comments (2)
Previously missed (2) — in code that hasn't changed since the last review.
marimo/_server/api/endpoints/export.py:352
- This rename guard runs before the awaited threaded file write. For a large export, the notebook can be renamed after this check while
_write_file_syncis still running, leaving the obsolete export at the old path despite the intended skip. Commit via a temporary file only after revalidating the captured filename on the event loop, or invalidate/cancel the reserved revision as part of rename handling.
frontend/src/components/slides/reveal-component.tsx:490 - The new WebAssembly-only startup branch is not exercised end to end: the added browser test covers static HTML, while the hook test only toggles the completion atom. Please add an
html-wasm --mode runbrowser test that opens a slides export at a deep link and interacts with a Python-backed control; otherwise regressions where the deck never resolves its hash or loses reactivity will pass the current suite.
const initialRunCompleted = useAtomValue(initialRunCompletedAtom);
const canResolveInitialHash = runtimeKind !== "wasm" || initialRunCompleted;
const deckPlugins = useMemo(
() => (kioskMode || !supportsSpeakerView(runtimeKind) ? [] : [RevealNotes]),
Light2Dark
left a comment
There was a problem hiding this comment.
looks good, just wording nits.
83fb15e to
6d75abb
Compare
Summary
A notebook configured as slides currently exports as a normal vertical notebook because its saved layout is not part of the exported mount data.
This PR carries the configured layout through static HTML and WebAssembly export, then restores it through the existing layout plugins before cells render. Static HTML opens as a Reveal deck with captured outputs. WebAssembly
--mode runopens the same deck and keeps Python-backed controls reactive in the browser.Both formats preserve slide types, fragments, speaker notes, deck settings, deep links, and code visibility. Ordinary notebooks retain their normal exported shell.
Implementation
Speaker view
Both formats embed speaker notes in the exported HTML, so anyone with the file can read them. Static HTML enables Reveal speaker view with
S.WebAssembly keeps speaker view disabled. Reveal creates the presenter previews by loading the deck twice in iframes, which would start two more Pyodide workers and rerun the notebook independently. Those previews could diverge from the main deck and repeat side effects. Clean support would need passive preview frames supplied by the main runtime, so this change keeps one Pyodide owner per deck. We can consider this as a follow up, but for now the effort-reward ratio seemed poor.
Closes #10682